///////////////////////////////////////////////////////////////////////////////
//
// lighthouse3D - 3D Software Library
//
// File: CCamera.cpp
//
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2003-2013 Marcus Reinhardt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
///////////////////////////////////////////////////////////////////////////////
//
// You can find a copy of the GNU General Public License (gpl.txt) in the
// directory of the library.
//
///////////////////////////////////////////////////////////////////////////////
#include "CCamera.h"

///////////////////////////////////////////////////////////////////////////////
// CCamera::CCamera( ... )
///////////////////////////////////////////////////////////////////////////////
CCamera::CCamera()
{
  activeCamera = 0;
  Cameras.clear();
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::~CCamera( ... )
///////////////////////////////////////////////////////////////////////////////
CCamera::~CCamera()
{
  activeCamera = 0;
  Cameras.clear();
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::getCameraInformation( ... )
///////////////////////////////////////////////////////////////////////////////
TCamera CCamera::getCameraInformation(int i)
{
   TCamera camInfo;

   if (i < 0)
       i = activeCamera;
       
   camInfo = Cameras[i];
   
   // Calculation of angles
   float angX = mathe3D.dotProduct(camInfo.rightVec, camInfo.origRightVec);
   if (angX < -1)
	   angX = -1;
   if (angX > 1)
	   angX = 1;
   float angY = mathe3D.dotProduct(camInfo.downVec, camInfo.origDownVec);
   if (angY < -1)
	   angY = -1;
   if (angY > 1)
	   angY = 1;
   float angZ = mathe3D.dotProduct(camInfo.viewVec, camInfo.origViewVec);
   if (angZ < -1)
	   angZ = -1;
   if (angZ > 1)
	   angZ = 1;
   camInfo.angleX = (float) (acos(angX)*180 / M_PI);
   camInfo.angleY = (float) (acos(angY)*180 / M_PI);
   camInfo.angleZ = (float) (acos(angZ)*180 / M_PI);

   return camInfo;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::fastRotateVector( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::fastRotateVector(TVector3D *v, float angleSin, float angleCos, int rotationAxis)
{
   TVector3D t;

   if (rotationAxis == 0)
   {
       t.d[0] = v->d[0];
       t.d[1] = v->d[1] * angleCos - v->d[2] * angleSin;
       t.d[2] = v->d[1] * angleSin + v->d[2] * angleCos;
   }

   if (rotationAxis == 1)
   {
       t.d[0] = v->d[0] * angleCos + v->d[2] * angleSin;
       t.d[1] = v->d[1];
       t.d[2] = v->d[2] * angleCos - v->d[0] * angleSin;
   }

   if (rotationAxis == 2)
   {
       t.d[0] = v->d[0] * angleSin - v->d[1] * angleCos;
       t.d[1] = v->d[0] * angleCos + v->d[1] * angleSin;
       t.d[2] = v->d[2];
   }
   
   v->d[0] = t.d[0];
   v->d[1] = t.d[1];
   v->d[2] = t.d[2];
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::getActiveCamera( ... )
///////////////////////////////////////////////////////////////////////////////
int CCamera::getActiveCamera()
{
  return activeCamera;

}

///////////////////////////////////////////////////////////////////////////////
// CCamera::getCameraCount( ... )
///////////////////////////////////////////////////////////////////////////////
int CCamera::getCameraCount()
{
  return(int) Cameras.size();
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::setActiveCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::setActiveCamera(int i)
{
  activeCamera = i;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::setNextCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::setNextCamera()
{
  activeCamera++;
  
  if ( activeCamera >= getCameraCount() )
      activeCamera = 0;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::resetActiveCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::resetActiveCamera()
{
  Cameras[activeCamera].viewVec.d[0] = 0;
  Cameras[activeCamera].viewVec.d[1] = 0;
  Cameras[activeCamera].viewVec.d[2] = 1;
  
  Cameras[activeCamera].downVec.d[0] = 0;
  Cameras[activeCamera].downVec.d[1] = 1;
  Cameras[activeCamera].downVec.d[2] = 0;

  Cameras[activeCamera].rightVec.d[0] = 1;
  Cameras[activeCamera].rightVec.d[1] = 0;
  Cameras[activeCamera].rightVec.d[2] = 0;

  Cameras[activeCamera].viewVec = mathe3D.normalizeVector(Cameras[activeCamera].viewVec);
  Cameras[activeCamera].downVec = mathe3D.normalizeVector(Cameras[activeCamera].downVec);
  Cameras[activeCamera].rightVec = mathe3D.normalizeVector(Cameras[activeCamera].rightVec);

  Cameras[activeCamera].origRightVec.d[0] = 1;
  Cameras[activeCamera].origRightVec.d[1] = 0;
  Cameras[activeCamera].origRightVec.d[2] = 0;
  Cameras[activeCamera].origDownVec.d[0] = 0;
  Cameras[activeCamera].origDownVec.d[1] = 1;
  Cameras[activeCamera].origDownVec.d[2] = 0;
  Cameras[activeCamera].origViewVec.d[0] = 0;
  Cameras[activeCamera].origViewVec.d[1] = 0;
  Cameras[activeCamera].origViewVec.d[2] = 1;

  Cameras[activeCamera].camOriginX = 0;    
  Cameras[activeCamera].camOriginY = 0;    
  Cameras[activeCamera].camOriginZ = 0;

  Cameras[activeCamera].trX = 0;
  Cameras[activeCamera].trY = 0;
  Cameras[activeCamera].trZ = 0;

  Cameras[activeCamera].oldtrX = 0;
  Cameras[activeCamera].oldtrY = 0;
  Cameras[activeCamera].oldtrZ = 0;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::addCamera( ... )
///////////////////////////////////////////////////////////////////////////////
bool CCamera::addCamera(TCamera &c)
{
  if (c.nearClipping > c.farClipping)
      return false;

  if (c.nearClipping < _minNearPlane_)
      c.nearClipping = _minNearPlane_;

  if (c.farClipping > _maxFarPlane_)
      c.farClipping = _maxFarPlane_;

  c.viewVec = mathe3D.normalizeVector(c.viewVec);
  c.downVec = mathe3D.normalizeVector(c.downVec);
  c.rightVec = mathe3D.normalizeVector(c.rightVec);
  
  c.origRightVec.d[0] = 1;
  c.origRightVec.d[1] = 0;
  c.origRightVec.d[2] = 0;
  c.origDownVec.d[0] = 0;
  c.origDownVec.d[1] = 1;
  c.origDownVec.d[2] = 0;
  c.origViewVec.d[0] = 0;
  c.origViewVec.d[1] = 0;
  c.origViewVec.d[2] = 1;

  c.camOriginX = 0;    
  c.camOriginY = 0;    
  c.camOriginZ = 0;

  c.colDetect = 0;

  Cameras.push_back(c);
  
  return true;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::getCamera( ... )
///////////////////////////////////////////////////////////////////////////////
TCamera *CCamera::getCamera(int i)
{
  return ( ((i < 0) || (i >= getCameraCount())) ? NULL : &Cameras[i]);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::transformVector( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::transformVector(TVector3D *vec, int i)
{
   TVector3D vecTmp;
   
   if (i < 0)
	   i = activeCamera;
   
   vecTmp.d[0] = vec->d[0];
   vecTmp.d[1] = vec->d[1];
   vecTmp.d[2] = vec->d[2];
   
   vec->d[0] = mathe3D.dotProduct(vecTmp, Cameras[i].rightVec);
   vec->d[1] = mathe3D.dotProduct(vecTmp, Cameras[i].downVec);
   vec->d[2] = mathe3D.dotProduct(vecTmp, Cameras[i].viewVec);
}


///////////////////////////////////////////////////////////////////////////////
// CCamera::turnUpCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnUpCamera(int i, int angle)
{
   float len, sinV, cosV;

   if (i < 0)
       i = activeCamera;

   if (angle < 0)
       angle = 360 + angle;

   angle = angle % 360;

   len = (float) hypot(Cameras[i].viewVec.d[0], Cameras[i].viewVec.d[2]);

   if ( len != 0)
   {
	   sinV = Cameras[i].viewVec.d[0] / len;
	   cosV = Cameras[i].viewVec.d[2] / len;

	   fastRotateVector(&Cameras[i].viewVec, -sinV, cosV, 1);
   }

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, angle, 0, 0);

   if ( len != 0)
   {
       fastRotateVector(&Cameras[i].viewVec, sinV, cosV, 1);
   }

   Cameras[i].downVec = mathe3D.crossProduct(Cameras[i].viewVec, Cameras[i].rightVec);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnDownCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnDownCamera(int i, int angle)
{
   float len, sinV, cosV;

   if (i < 0)
	   i = activeCamera;

   angle = 360 - angle;
   if (angle < 0)
	   angle = 360 + angle;

   angle = angle % 360;

   len = (float) hypot(Cameras[i].viewVec.d[0], Cameras[i].viewVec.d[2]);

   if ( len != 0 )
   {
	   sinV = Cameras[i].viewVec.d[0] / len;
	   cosV = Cameras[i].viewVec.d[2] / len;

	   fastRotateVector(&Cameras[i].viewVec, -sinV, cosV, 1);
   }

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, angle, 0, 0);

   if ( len != 0)
   {
	   fastRotateVector(&Cameras[i].viewVec, sinV, cosV, 1);
   }

   Cameras[i].downVec = mathe3D.crossProduct(Cameras[i].viewVec, Cameras[i].rightVec);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::setColDistance( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::setColDistance(int i, float d)
{
   if (i < 0)
	   i = activeCamera;

   Cameras[i].colDetect = d;

}

///////////////////////////////////////////////////////////////////////////////
// CCamera::getColDistance( ... )
///////////////////////////////////////////////////////////////////////////////
float CCamera::getColDistance(int i)
{
   if (i < 0)
	   i = activeCamera;

   return Cameras[i].colDetect;

}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnRightCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnRightCamera(int i, int angle)
{
   if (i < 0)
	   i = activeCamera;

   angle = 360 - angle;
   if (angle < 0)
	   angle = 360 + angle;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, angle, 0);

   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, angle, 0);

   Cameras[i].downVec = mathe3D.crossProduct(Cameras[i].viewVec, Cameras[i].rightVec);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnLeftCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnLeftCamera(int i, int angle)
{
   if (i < 0)
	   i = activeCamera;

   if (angle < 0)
	   angle = 360 + angle;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, angle, 0);

   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, angle, 0);

   Cameras[i].downVec = mathe3D.crossProduct(Cameras[i].viewVec, Cameras[i].rightVec);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundXAxisPositiv( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundXAxisPositiv(int i, int angle)
{
   if (i < 0)
       i = activeCamera;

   if ( (angle < 0) || (angle > 359) )
       return;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, angle, 0, 0);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, angle, 0, 0);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, angle, 0, 0);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundXAxisNegativ( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundXAxisNegativ(int i, int angle)
{
   if (i < 0)
       i = activeCamera;
   
   if ( (angle < 0) || (angle > 359) )
       return;

   angle = 360 - angle;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, angle, 0, 0);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, angle, 0, 0);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, angle, 0, 0);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundYAxisPositiv( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundYAxisPositiv(int i, int angle)
{
   if (i < 0)
       i = activeCamera;

   if ( (angle < 0) || (angle > 359) )
       return;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, angle, 0);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, 0, angle, 0);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, angle, 0);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundYAxisNegativ( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundYAxisNegativ(int i, int angle)
{
   if (i < 0)
       i = activeCamera;
   
   if ( (angle < 0) || (angle > 359) )
       return;

   angle = 360 - angle;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, angle, 0);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, 0, angle, 0);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, angle, 0);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundZAxisPositiv( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundZAxisPositiv(int i, int angle)
{
   if (i < 0)
       i = activeCamera;

   if ( (angle < 0) || (angle > 359) )
       return;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, 0, angle);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, 0, 0, angle);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, 0, angle);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::turnAroundZAxisNegativ( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::turnAroundZAxisNegativ(int i, int angle)
{
   if (i < 0)
       i = activeCamera;
   
   if ( (angle < 0) || (angle > 359) )
	   return;

   angle = 360 - angle;

   angle = angle % 360;

   Cameras[i].viewVec = mathe3D.rotateVector(Cameras[i].viewVec, 0, 0, angle);
   Cameras[i].downVec = mathe3D.rotateVector(Cameras[i].downVec, 0, 0, angle);
   Cameras[i].rightVec = mathe3D.rotateVector(Cameras[i].rightVec, 0, 0, angle);
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::undoLastMove( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::undoLastMove(int i)
{
   if (i < 0)
	   i = activeCamera;

   Cameras[i].trX = Cameras[i].oldtrX;
   Cameras[i].trY = Cameras[i].oldtrY;
   Cameras[i].trZ = Cameras[i].oldtrZ;
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveAheadCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveAheadCamera(int i, float steps)
{
   TVector3D vt;

   if (i < 0)
	   i = activeCamera;

   vt = mathe3D.multSkVector(steps, Cameras[i].viewVec);

   Cameras[i].oldtrX = Cameras[i].trX;
   Cameras[i].oldtrY = Cameras[i].trY;
   Cameras[i].oldtrZ = Cameras[i].trZ;

   Cameras[i].trX -= vt.d[0];
   Cameras[i].trY -= vt.d[1];
   Cameras[i].trZ -= vt.d[2];
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveBackCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveBackCamera(int i, float steps)
{
   TVector3D vt;

   if (i < 0)
	   i = activeCamera;

   vt = mathe3D.multSkVector(steps, Cameras[i].viewVec);

   Cameras[i].oldtrX = Cameras[i].trX;
   Cameras[i].oldtrY = Cameras[i].trY;
   Cameras[i].oldtrZ = Cameras[i].trZ;

   Cameras[i].trX += vt.d[0];
   Cameras[i].trY += vt.d[1];
   Cameras[i].trZ += vt.d[2];
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveLeftCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveLeftCamera(int i, float steps)
{
   TVector3D vt;

   if (i < 0)
	   i = activeCamera;

   vt = mathe3D.multSkVector(steps, Cameras[i].rightVec);

   Cameras[i].trX += vt.d[0];
   Cameras[i].trY += vt.d[1];
   Cameras[i].trZ += vt.d[2];
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveRightCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveRightCamera(int i, float steps)
{
   TVector3D vt;
   
   if (i < 0)
       i = activeCamera;
   
   vt = mathe3D.multSkVector(steps, Cameras[i].rightVec);
   
   Cameras[i].trX -= vt.d[0];
   Cameras[i].trY -= vt.d[1];
   Cameras[i].trZ -= vt.d[2];  
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveUpCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveUpCamera(int i, float steps)
{
   TVector3D vt;
   
   if (i < 0)
       i = activeCamera;
   
   vt = mathe3D.multSkVector(steps, Cameras[i].downVec);
   
   Cameras[i].trX -= vt.d[0];
   Cameras[i].trY -= vt.d[1];
   Cameras[i].trZ -= vt.d[2];  
}

///////////////////////////////////////////////////////////////////////////////
// CCamera::moveDownCamera( ... )
///////////////////////////////////////////////////////////////////////////////
void CCamera::moveDownCamera(int i, float steps)
{
   TVector3D vt;
   
   if (i < 0)
       i = activeCamera;
   
   vt = mathe3D.multSkVector(steps, Cameras[i].downVec);
   
   Cameras[i].trX += vt.d[0];
   Cameras[i].trY += vt.d[1];
   Cameras[i].trZ += vt.d[2];  
}

